https://datatracker.ietf.org/doc/html/rfc5246

client random のことは文中の疑似コードで client_random として登場することがある。
client random は SecurityParameters に含まれていて、その型は opaque となっている。opaque は https://datatracker.ietf.org/doc/html/rfc5246#section-4.2 において “Single-byte entities containing uninterpreted data are of type opaque.” と述べられている。すなわち、「未解釈のデータを持つ 1 バイトの値の型」ということか。client_randomopaque[32] 型を持つので 32 バイトの値であろうということがわかる。これは SSLKEYLOGFILE 環境変数を指定して得られるファイル内の CLIENT_RANDOM の行において 2 つ目の項目が 16 進数を表す長さ 64 の文字列で表現されていることと辻褄が合う。


Security Parameters

https://datatracker.ietf.org/doc/html/rfc5246#appendix-A.6
セキュリティパラメータは TLS handshake で決定され、TLS record layer が接続状態を初期化するために与えられるパラメータである。
この中に master secret, client random, server random が入っている。


TLSCiphertext

TLSCompressed を暗号化したもの。

TLSCompressed

TLSPlaintext 構造を圧縮したもので、圧縮後のデータのことを fragment と呼んでいる。また、その長さは length というフィールドで保持されており、この struct の最後に配置されている(variable length な構造体あるある)。セッションごとに圧縮用アルゴリズムが定められる。それらのアルゴリズムは Transport Layer Security Protocol Compression Methods (RFC 3749) で定められているらしい。CompressionMethod.null は圧縮関数として恒等関数を選択したことになる。implementation note として、解凍関数はその処理の内部でバッファオーバーフローを起こさないことについて責任を持つらしい。わざわざ使用の中でそれぞれの役割における責任まで定めてくれるとは知らなかった。ActivityPub の仕様書が色々と投げやりすぎたのに慣れてしまっているのかもしれない。
圧縮は当然ながらロスレスでなければならない。加えて、
length を 1024 バイト以上に増やしてもならない。解凍関数は、与えられた TLSCompressed.fragment について解凍後のサイズが 16384 バイトを超える場合は fatal error としなければならないらしい。


AEAD Ciphers

https://datatracker.ietf.org/doc/html/rfc5246#section-6.2.3.3
GCM は AEAD の一種なので、decryption process を追うにあたってはこのあたりを読んでおく必要がありそう。
AEAD の場合、復号して得られる TLSCompressed.fragment は AEAD-Decrypt(write_key, nonce, AEADEncrypted, additional_data) で表されるらしい。write_key はそれぞれ client_write_key または server_write_key である。
additional_data = seq_num + TLSCompressed.type + TLSCompressed.version + TLSCompressed.length であることから、解読には seq_num が必要であり、その定義は


Key Calculation


https://datatracker.ietf.org/doc/html/rfc5246#section-6.3
master secret は key_block と呼ばれる秘密のバイト列に展開され、その列から以下の要素がこの順番に得られる。ただし、カッコで囲まれているところは必要な場合にのみ取り出されて利用される(AEAD 暗号の場合は使われる)。

  • client_write_MAC_key
  • server_write_MAC_key
  • client_write_key
  • server_write_key
  • (client_write_IV)
  • (server_write_IV)


key_block は PRF を master_secret, server_random, client_random と共に呼び出すことで得られる。server_random は ServerKeyExchange の中に含まれている。

Connection States


https://datatracker.ietf.org/doc/html/rfc5246#section-6.1
TLS 接続状態は、TLS Record Protocol の動作環境である。圧縮アルゴリズム、暗号化アルゴリズム、そして MAC 算出アルゴリズムを定める。さらに:

  • MAC key
  • 読み書きの両方向のための接続で使われる bulk encryption keys
    PRF: Pseudo-Random Function
    key_block の計算は rustls だとどこで行われているのだろうか💭
  • https://github.com/rustls/rustls/blob/078f03334b2de9d8e464755d8836318b9a221346/rustls/src/tls12/mod.rs#L251 ここっぽい!
    • make_key_block の中で key_block は作られていそう(当然)
      • 雑談:仕様の中で "key expansion" を PRF に渡していたのは、本当に実装の中でもこの文字列に対応するバイト列が secret を得るためのデータの一部として渡されていて衝撃的


seq_num の定義


読み書きのそれぞれの状態のために保持されている、各接続状態に含まれるもの。接続状態が active になったときには必ずゼロにセットされなければならない。
uint64 で 2^64-1 を超えないし折り返しも起きない。もしオーバーフローしそうな加算があるなら、TLS のネゴシエーションから再開する。
レコードごとにインクリメントされる。
最初に転送されたレコードは sequence number として 0 を使わなければならない。


rustls の実装では RecordLayerwrite_seq, read_seq として両者を保持しており、それぞれ encrypt または decrypt するときにインクリメントされている。
参考:https://github.com/rustls/rustls/blob/4d1b762b5328a1714862ba73ec72d5522fe0c049/rustls/src/record_layer.rs#L56-L103